package com.liser.southernstar.utils;

import androidx.annotation.NonNull;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

/**
 * https请求，只支持byte传输。<br/>
 * 本类超时时间单独被设置为读时间与写时间，连接时间使用默认设置
 * 实例会保持最最初创建的sessionID
 */
public class HttpHandle {

    private static HttpHandle instance = null;

    // 默认读写超时时间
    int defaultTimeOut = 5000;

    /**
     * 默认媒体类型
     */
    public static final MediaType DefaultMediaType = MediaType.parse("application/octet-stream");

    // 缓存sessionID
    private OkHttpClient _httpClient = new OkHttpClient.Builder()
            .cookieJar(new CookieJar() {
                private final String defaultSession = UUID.randomUUID().toString();
                private Map<String, List<Cookie>> domainCookies = new HashMap<>();

                @Override
                public void saveFromResponse(@NonNull HttpUrl url, @NonNull List<Cookie> cookies) {
                    this.domainCookies.put(url.host(), cookies);
                }

                @Override
                public List<Cookie> loadForRequest(@NonNull HttpUrl url) {
                    String domain = url.host();
                    List<Cookie> result = this.domainCookies.get(domain);
                    if (result == null) {
                        result = new ArrayList<>(1);
                        result.add(new Cookie.Builder().name("SESSION").value(defaultSession).domain(domain).build());
                        this.domainCookies.put(domain, result);
                    }
                    return result;
                }
            })
            .readTimeout(defaultTimeOut, TimeUnit.MILLISECONDS).writeTimeout(defaultTimeOut, TimeUnit.MILLISECONDS)
            .build();

    private HttpHandle() {
    }

    public static HttpHandle getInstance() {
        if (instance == null) {
            instance = new HttpHandle();
        }
        return instance;
    }

    /**
     * post请求
     */
    public byte[] post(Urls url, byte[] data, MediaType mediaType) throws IOException {
        return post(url, data, defaultTimeOut, mediaType);
    }

    /*** post请求*/
    public byte[] post(Urls url, byte[] data) throws IOException {
        return post(url, data, defaultTimeOut, DefaultMediaType);
    }

    /**
     * post请求
     */
    public byte[] post(Urls url, byte[] data, int millionSeconds, MediaType mediaType) throws IOException {
        Request httpRequest = new Request.Builder().url(url.getUrl())
                .post(RequestBody.create(mediaType, data)).build();
        return requestAct(httpRequest, null, millionSeconds);
    }

    /**
     * post请求，可通过此接口下载大量数据
     */
    public byte[] post(Urls url, byte[] data, InputStreamHandler inHandler, int millionSeconds, MediaType mediaType) throws IOException {
        Request httpRequest = new Request.Builder().url(url.getUrl())
                .post(RequestBody.create(mediaType, data)).build();
        return requestAct(httpRequest, null, millionSeconds);
    }

    /**
     * post请求，可通过此接口上传大量数据
     */
    public byte[] post(Urls url, RequestBody requestBody, int millionSeconds) throws IOException {
        Request httpRequest = new Request.Builder().url(url.getUrl()).post(requestBody).build();
        return requestAct(httpRequest, null, millionSeconds);
    }

    /**
     * post请求，可通过此接口上传下载大量数据
     */
    public byte[] post(Urls url, RequestBody requestBody, InputStreamHandler inHandler, int millionSeconds) throws IOException {
        Request httpRequest = new Request.Builder().url(url.getUrl()).post(requestBody).build();
        return requestAct(httpRequest, inHandler, millionSeconds);
    }

    /**
     * get请求
     */
    public byte[] get(Urls url) throws IOException {
        return get(url, defaultTimeOut);
    }

    /**
     * get请求
     */
    public byte[] get(Urls url, int millionSeconds) throws IOException {
        Request httpRequest = new Request.Builder().url(url.getUrl()).get().build();
        return requestAct(httpRequest, null, millionSeconds);
    }

    /**
     * get请求，自行处理输入流
     */
    public byte[] get(Urls url, InputStreamHandler inHandler, int millionSeconds) throws IOException {
        Request httpRequest = new Request.Builder().url(url.getUrl()).get().build();
        return requestAct(httpRequest, inHandler, millionSeconds);
    }

    /**
     * 根据超时时间获取执行请求的client对象
     */
    private OkHttpClient getExcuteClient(int millionSeconds) {
        OkHttpClient excuteClient;
        if (millionSeconds != defaultTimeOut) {
            // 与默认client共享连接池与调度器
            excuteClient = _httpClient.newBuilder()
                    .readTimeout(millionSeconds, TimeUnit.MILLISECONDS)
                    .writeTimeout(millionSeconds, TimeUnit.MILLISECONDS)
                    .build();
        } else {
            excuteClient = _httpClient;
        }
        return excuteClient;
    }

    /**
     * 发送HTTP请求
     */
    private byte[] requestAct(Request request, InputStreamHandler inHandler, int millionSeconds) throws IOException {
        Response httpResponse = null;
        try {
            httpResponse = getExcuteClient(millionSeconds).newCall(request).execute();
            okhttp3.ResponseBody httpResponseBody = httpResponse.body();
            if (httpResponseBody == null) {
                throw new RuntimeException();
            }
            // 是否自行处理输入流
            if (inHandler != null) {
                inHandler.in(httpResponseBody.byteStream());
                return new byte[0];
            } else {
                return httpResponseBody.bytes();
            }
        } catch (IOException e) {
            throw e;
        } finally {
            if (httpResponse != null) {
                httpResponse.close();
            }
        }
    }

    /**
     * url规范化
     */
    public interface Urls {
        String getUrl(Object... keyVal);
    }

    /**
     * 传入此接口以接收输出流
     */
    public interface InputStreamHandler {
        void in(InputStream in) throws IOException;
    }
}
